from .cement_batching import (transform_ci_to_nl, convert_str_to_float,
							 integrate_gypsum_into_chemical, wet_to_dry, calc_balance_dry_from_feedback_dry,get_coef)
from . import config
from django.conf import settings
import pymongo
import numpy as np

def process_adm_ratio_in_predict(current_data,adm_ratio):

	if adm_ratio != {}:
		feedback_wet = current_data["dcs_feedback"]

		for k, v in adm_ratio.items():
			sum_num_feedback = feedback_wet[k]

			deno = sum(v.values())
			for k_1, v_1 in v.items():
				feedback_wet[k_1] = sum_num_feedback * v_1 / deno

def predict_cement_strength(model_type, model_code, data):

	# 1.2 类型转换
	data = convert_str_to_float(data)
	# 1.3 扁平化处理  
	(clinker_strength, gyp_ratio, admixture_ratio, materials_chemical_dict, current_data) = data.values()    #增加一天实测值

	current_process_quality = current_data['process_quality']
	current_DCS_feedback = current_data['dcs_feedback']

	process_adm_ratio_in_predict(current_data, admixture_ratio)

	# 1.2 数据校验
	# 化学成份 价格

	cement_1_d = 0 
	cement_3_d = 0

	chemical_key = set(data['materials_chemical_dict'].keys())
	if model_type == 2:
		cement_3_d = current_data["cement_real_strength_3d"]
	elif model_type == 1:
		cement_1_d = current_data["cement_real_strength_1d"]

	gyp_key = set(data['gyp_ratio'].keys())
	adm_key = set(data["admixture_ratio"].keys())
	for k,v in data["admixture_ratio"].items():
		adm_key =adm_key.union(set(v.keys()))

	# 1.3 石膏
	# 抽象石膏
	abs_gyp_flag = True if "石膏" in current_DCS_feedback.keys() else False
	if abs_gyp_flag:

		integrate_gypsum_into_chemical(materials_chemical_dict, gyp_ratio)

	# 具体石膏
	con_gyp_flag = False
	con_gyp_list = []
	gypsum_threshold = config.gyp_thre
	for i in materials_chemical_dict.keys():
		if materials_chemical_dict[i]["SO3"] > gypsum_threshold and i in current_DCS_feedback.keys():
			con_gyp_flag = True
			con_gyp_list.append(i)

	# 2 强度预测
	# 2.1 转换为物料平衡干基
	materials_chemical_dict = {material: {key: value / 100 for key, value in properties.items()} for
							   material, properties in materials_chemical_dict.items()}
	print(current_DCS_feedback)
	print("materials_chemical_dict:",materials_chemical_dict)
	feedback_dry = wet_to_dry(current_DCS_feedback, materials_chemical_dict)

	print("feedback_dry",feedback_dry)

	balance_dry = calc_balance_dry_from_feedback_dry(feedback_dry, current_process_quality, materials_chemical_dict)

	# 取系数逻辑   
	app_set = chemical_key - gyp_key
	params_3d, params_28d, intercept_3d, intercept_28d, cement_list = get_coef(model_type, model_code, app_set, materials_chemical_dict,abs_gyp_flag,con_gyp_flag,con_gyp_list)   #增加一天实测值
	print("本次涉及到的物料有：",app_set)
	print("使用params_3d",params_3d)
	print("使用params_28d",params_28d)
	print("intercept_3d",intercept_3d)
	print("intercept_28d",intercept_28d)

	materials_list = app_set
	process_list = ["细度45μm", "比表", "SO3"]

	strength_3d, strength_28d = calc_strength(clinker_strength, model_type, balance_dry, current_process_quality, params_3d, params_28d, materials_list, process_list,cement_1_d,cement_3_d, cement_list, intercept_3d, intercept_28d)

	return strength_3d, strength_28d

def calc_strength(clinker_strength, model_type, balance_dry, process, strength_3d_params, strength_28d_params, materials_list, process_list, cement_1_d, cement_3_d, cement_list, intercept_3d, intercept_28d):
	'''
	计算水泥强度，有两种方法： 根据SO3的计算方法不同，采用不同的计算方法
	1. SO3 根据物料计算而来）
	'''
	strength_3d = 0
	strength_28d = 0
	flag = True   #增加一天实测值
	balance_dry = {k: v/100 for k, v in balance_dry.items()}
	for mat in materials_list : 
		try:
			strength_3d += balance_dry[mat] * strength_3d_params[mat] * clinker_strength['熟料3天强度预测']
			strength_28d += balance_dry[mat] * strength_28d_params[mat] * clinker_strength['熟料28天强度预测']
		except KeyError:
			flag = False
			break
	if flag:
		for i in process_list:
			strength_3d += process[i] * strength_3d_params[i]
			strength_28d += process[i] * strength_28d_params[i]
	print("strength_3d",strength_3d)
	print("strength_28d", strength_28d)
	if model_type == 1:
		strength_3d = isNone(flag,strength_3d,clinker_strength['熟料3天强度预测'],intercept_3d,cement_1_d*cement_list[0])
		strength_28d = isNone(flag,strength_28d,clinker_strength['熟料28天强度预测'],intercept_28d,cement_1_d*cement_list[1])
	else :
		strength_3d = isNone(flag,strength_3d,clinker_strength['熟料3天强度预测'],intercept_3d,0)
		strength_28d = isNone(flag,strength_28d,clinker_strength['熟料28天强度预测'],intercept_28d,0)
		if model_type == 2:
			strength_28d = strength_28d+cement_3_d*cement_list[0]

	return strength_3d, strength_28d

def float_int(x):
	if isinstance(x, float):
		return int(x)

def isNone(flag,x1,x2,x3,x4):
	if flag:
		return x1 + x2 + x3 + x4    #增加一天实测值
	else:
		return 0
	
def cal_acc(data):
	# 初始化计数器
	total_3d = 0
	correct_3d = 0
	total_28d = 0
	correct_28d = 0

	# 遍历数组，计算 3 天和 28 天预测的准确率
	for row in data:
		if not row:
			continue
		actual_3d = row[1]
		predicted_3d = row[2]
		actual_28d = row[3]
		predicted_28d = row[4]
		
		# 计算 3 天预测的准确率
		if actual_3d is not None and predicted_3d is not None:
			try:
				actual_3d = float(actual_3d)
				total_3d += 1
				if abs(actual_3d - predicted_3d) <= 1.2:  # 假设预测值与实测值的误差在 1 .2以内视为准确
					correct_3d += 1
			except ValueError:
				print(f"无法将 actual_3d 转换为浮点数: {actual_3d}")
		
		# 计算 28 天预测的准确率
		if actual_28d is not None and predicted_28d is not None:
			try:
				actual_28d = float(actual_28d)
				total_28d += 1
				if abs(actual_28d - predicted_28d) <= 1.5:  # 假设预测值与实测值的误差在 1.5 以内视为准确
					correct_28d += 1
			except ValueError:
				print(f"无法将 actual_28d 转换为浮点数: {actual_28d}")

	# 计算准确率
	accuracy_3d = correct_3d / total_3d if total_3d > 0 else 0
	accuracy_28d = correct_28d / total_28d if total_28d > 0 else 0

	return accuracy_3d,accuracy_28d

def result_plt(y,y_preds,thresher,filename):# -> tuple[Float | ndarray, Float | ndarray, Float | ndarray, ...:

	import matplotlib.pyplot as plt
	from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
	
	# 检查并处理 NaN 值
	valid_indices = ~np.isnan(y) & ~np.isnan(y_preds)
	y = y[valid_indices]
	y_preds = y_preds[valid_indices]
	# Evaluation metrics
	mse = mean_squared_error(y, y_preds)
	mae = mean_absolute_error(y, y_preds)
	r2 = r2_score(y, y_preds)
	within_threshold = (np.abs(y - y_preds) <= thresher) * 100
	percentage_within_threshold = np.mean(within_threshold)
	mean = y.mean()
	std_dev = y.std()
	cv = (std_dev / mean) * 100

	# Plotting
	fig_width = max(10, len(list(y_preds)) / 5)  # 宽度至少为10，高度可以根据 y 的长度变化
	fig_height = 7  # 高度固定为7
	plt.figure(figsize=(fig_width, fig_height))
	plt.plot(y, label='True Values', marker='o')
	plt.plot(y_preds, label='Predictions', marker='x')

	for i, true_value in enumerate(y):
		plt.plot([i, i], [true_value - thresher, true_value + thresher], color='grey', linestyle='--', linewidth=0.5)
	plt.title(f'MSE: {mse:.2f}, MAE: {mae:.2f}, R^2: {r2:.2f}\n Global ±{thresher:.1f}: {percentage_within_threshold:.2f}%, CV: {cv:.2f}%')
	plt.xlabel('Sample Index')
	plt.ylabel('Y Value')
	plt.legend()
	# 自动调整布局
	plt.tight_layout()

	plt.show()
	return mse, mae, r2, percentage_within_threshold

def cement_strength_predict(model_code, item):
	# 初始化一个空字典
	result = {
		"linear": {
			"no_real_strength": {},
			"with_real_strength_1d": {},
			"with_real_strength_3d": {}
		}
	}
	# 定义键的列表
	keys = ['CI_10001103', 'CI_10001104']
	data = {
				"clinker_strength": {
					"熟料3天强度预测": item['clinker_strength']['熟料3天强度预测'],
					"熟料28天强度预测": item['clinker_strength']['熟料28天强度预测']
				},
				"gyp_ratio": item['gyp_ratio'],
				"admixture_ratio": item["admixture_ratio"],
				"materials_chemical_dict": item['materials_chemical_dict'],
				"current_data": {
					"process_quality": item["current_data"]['process_quality'],
					"dcs_feedback": item["current_data"]['dcs_feedback'],
					"cement_real_strength_1d": item["current_data"]['cement_real_strength_1d'],
					"cement_real_strength_3d": item["current_data"]['cement_real_strength_3d']
				}
	}
	strength_3d_linear, strength_28d_linear = predict_cement_strength(0, model_code, data)
	result["linear"]["no_real_strength"] = {keys[0]: str(strength_3d_linear), keys[1]: str(strength_28d_linear)}
	if data["current_data"]["cement_real_strength_1d"] is not None and data["current_data"]["cement_real_strength_3d"] is None:
		strength_3d_linear_1d, strength_28d_linear_1d = predict_cement_strength(1, model_code, data)
		result["linear"]["with_real_strength_1d"] = {keys[0]: str(strength_3d_linear_1d), keys[1]: str(strength_28d_linear_1d)}
	if data["current_data"]["cement_real_strength_1d"] is not None and data["current_data"]["cement_real_strength_3d"] is not None:
		strength_3d_linear_3d, strength_28d_linear_3d = predict_cement_strength(2, model_code, data)
		result["linear"]["with_real_strength_3d"] = {keys[0]: str(strength_3d_linear_3d), keys[1]: str(strength_28d_linear_3d)}
	
	return result
		  

if __name__ == '__main__':

	client = pymongo.MongoClient(settings.default_mongo_uri)
	db_handle = client[settings.database_name]  # 获取数据库句柄
	collection = db_handle[settings.self_learning_collection_name]

	# 查询集合中的所有文档

	cursor = collection.find({}, {"_id": 0})
	# 获取文档的字段数量
	data = list(cursor)

	# data = data_process.cut_data(data)

	cement_3_list = []
	cement_28_list = []
	cement_3_predict = []
	cement_28_predict = []
	for item in data:
		row = []
    # 在这里处理每个 item
	# 提取并整理数据
		if item['checkStrength3d'] is not None or item['checkStrength28d'] is not None:
			data = {
				"clinker_strength": {
					"CI_10001150": item['clinkerStrengthPrediction3d'],
					"CI_10001147": item['clinkerStrengthPrediction28d']
				},
				"gyp_ratio": item['gypRatio'],
				"cement_1_d": item['checkStrength1d'],   #增加一天实测值
				"cement_3_d": item["checkStrength3d"],
				"admixture_ratio": item["admixtureRatio"],
				"materials_chemical_dict": item['materialAnalysis'],
				"current_data": {
					"process_quality": item['processQuality'],
					"dcs_feedback": item['feedbackIngredient']
				}
			}
			strength_3d, strength_28d = predict_cement_strength(data)
			if strength_3d != 0 and strength_28d !=0:
				cement_3_list.append(item['checkStrength3d'])
				cement_3_predict.append(strength_3d)
				cement_28_list.append(item['checkStrength28d'])
				cement_28_predict.append(strength_28d)
	client.close()
	mse, mae, r2, percentage_within_threshold = result_plt(np.array(cement_3_list).astype(float),np.array(cement_3_predict).astype(float),1.2,"遵义赛德/linear_3d_1d_all.png")
	print(f"3天水泥强度预测结果:mse:{mse},mae:{mae},r_2:{r2},准确率：{percentage_within_threshold}")
	mse, mae, r2, percentage_within_threshold = result_plt(np.array(cement_28_list).astype(float),np.array(cement_28_predict).astype(float),1.5,"遵义赛德/linear_28d_1d_all.png")
	print(f"28天水泥强度预测结果:mse:{mse},mae:{mae},r_2:{r2},准确率：{percentage_within_threshold}")
